import torch
import numpy as np

def get_area(boxes):
    return (boxes[:,2] - boxes[:,0]) * (boxes[:,3] - boxes[:,1])

def get_intersection_area_matrix(proposals, gt_boxes):
    """
    return area matrix of shape (len(proposals), len(gt_boxes))
    """
    inter_w = np.clip(np.minimum(np.expand_dims(proposals[:,2], axis=1), np.expand_dims(gt_boxes[:,2], axis=0)) - np.maximum(np.expand_dims(proposals[:,0], axis=1), np.expand_dims(gt_boxes[:,0], axis=0)), 0, None)
    inter_h = np.clip(np.minimum(np.expand_dims(proposals[:,3], axis=1), np.expand_dims(gt_boxes[:,3], axis=0)) - np.maximum(np.expand_dims(proposals[:,1], axis=1), np.expand_dims(gt_boxes[:,1], axis=0)), 0, None)
    return inter_w * inter_h

def evaluate_matrix(matrix, threshold):
    """
    input matrix of shape (len(proposals), len(gt_boxes))
    return TP, FP, FN
    """
    matrix = (matrix > threshold)
    proposals_hit = matrix.any(axis=1)
    box_hit = matrix.any(axis=0)
    TP = proposals_hit.sum()     # number of box contain target
    FP = (~proposals_hit).sum()  # number of box not contain target
    FN = (~box_hit).sum()        # number of target not be detected
    return TP, FP, FN

def evaluate_matrix2(matrix, threshold):
    """
    input matrix of shape (len(proposals), len(gt_boxes))
    return TP, FP, FN
    """
    matrix = (matrix > threshold)
    proposals_hit = matrix.any(axis=1)
    box_hit = matrix.any(axis=0)
    TP = proposals_hit.sum()     # number of box contain target
    FP = (~proposals_hit).sum()  # number of box not contain target
    FN = (~box_hit).sum()        # number of target not be detected
    TP_gt = box_hit.sum()        # number of target be detected
    return TP, TP_gt, FP, FN

def get_precision(TP, FP, FN):
    return TP / (TP + FP) if TP + FP > 0 else 1.0

def get_recall(TP, FP, FN):
    return TP / (TP + FN) if TP + FN > 0 else 1.0
